﻿namespace NTLib.Core.Pool
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Pool used to managed the item according to the real type
    /// </summary>
    public class TypePool : BasePool
    {
        #region Helper

        /// <summary>
        /// Used Only as default object for the reflection manipulation
        /// </summary>
        /// <seealso cref="NTLib.Core.Pool.IPoolItem" />
        public class NullPoolItem : IPoolItem
        {
            /// <summary>
            /// Occurs when the item disposed.
            /// </summary>
            public event Action<object> ItemDisposed;

            /// <summary>
            /// Destroys this instance.
            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// This method must be called only by the pool managed when the instance is no more needed.
            /// </summary>
            /// <exception cref="NotImplementedException"></exception>
            /// <remarks>
            /// This methods is used to free all the memory used by the object.
            /// </remarks>
            /// <permission cref="IPool">This Method must be called only by the pool manager.</permission>
            public void Destroy()
            {
                throw new NotImplementedException();
            }

            /// <summary>
            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// After the call of this method the object would be reinjected in the (<see cref="IPool" />) owner's pool to be reuse.
            /// </summary>
            /// <exception cref="NotImplementedException"></exception>
            public void Dispose()
            {
                if (ItemDisposed != null)
                    ItemDisposed(this);
                throw new NotImplementedException();
            }

            /// <summary>
            /// Resets this instance.
            /// Everything MUST be initialized on this method.
            /// This method must be called only by the pool manager to initialize or reset the object.
            /// </summary>
            /// <param name="poolInstance">Instance of the pool where the object is attach.</param>
            /// <exception cref="NotImplementedException"></exception>
            /// <remarks>
            /// In this method you must clear all the data of your object.
            /// </remarks>
            /// <permission cref="IPool">This Method must be called only by the pool manager.</permission>
            public void Reset(IPool poolInstance)
            {
                throw new NotImplementedException();
            }
        }

        #endregion

        #region Fields

        private readonly Dictionary<Type, Queue<IPoolItem>> _freeItems;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes a new instance of the <see cref="TypePool"/> class.
        /// </summary>
        public TypePool()
        {
            this._freeItems = new Dictionary<Type, Queue<IPoolItem>>();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Frees the objects count.
        /// Must be used only for the Unit Test
        /// </summary>
        public uint FreeObjectsCount<U>()
        {
            lock (this._syncPoolObject)
            {
                Queue<IPoolItem> items = null;
                if (this._freeItems.TryGetValue(typeof(U), out items))
                    return (uint)items.Count;
                return 0;
            }
        }


        /// <summary>
        /// Cleans all.
        /// </summary>
        /// <returns>
        /// Return all the items in the recycle storage to be destroy
        /// </returns>
        protected override ICollection<IPoolItem> CleanAll()
        {
            List<IPoolItem> toDestroy = null;

            foreach (var freeObjects in this._freeItems)
            {
                var queue = freeObjects.Value;
                var minItems = MinItems;
                if (queue.Count < minItems)
                {
                    for (int i = queue.Count; i < minItems; ++i)
                        queue.Enqueue((IPoolItem)Activator.CreateInstance(freeObjects.Key));
                }

                var maxitem = MaxItems;

                if (queue.Count > maxitem)
                {
                    if (toDestroy == null)
                        toDestroy = new List<IPoolItem>();
                    for (int i = queue.Count; i > maxitem; --i)
                        toDestroy.Add(queue.Dequeue());
                }
            }
            return toDestroy;
        }

        /// <summary>
        /// Gets item from the recycle storage
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns>
        ///   <c>T Instance</c> if the recycle container contains one; otherwise <c>null</c>
        /// </returns>
        protected override IPoolItem GetItemFromRecycle<T>()
        {
            return GetItemFromRecycle(typeof(T));
        }

        /// <summary>
        /// Gets item from the recycle storage
        /// </summary>
        /// <param name="type"></param>
        /// <returns>
        ///   <c>T Instance</c> if the recycle container contains one; otherwise <c>null</c>
        /// </returns>
        protected override IPoolItem GetItemFromRecycle(Type type)
        {
            Queue<IPoolItem> freeItem = null;
            if (this._freeItems.TryGetValue(type, out freeItem) && freeItem.Count > 0)
                return freeItem.Dequeue();

            return null;
        }

        /// <summary>
        /// Removes the item from recycle storage.
        /// </summary>
        /// <param name="item">The item to remove from the recycle storage</param>
        protected override void RemoveItemFromRecycle(IPoolItem item)
        {
            Type iType = item.GetType();
            Queue<IPoolItem> freeItems = null;

            if (this._freeItems.TryGetValue(iType, out freeItems) && freeItems.Contains(item))
            {
                var safeItems = freeItems.Where(el => (IPoolItem)el != item).ToArray();
                freeItems.Clear();

                foreach (var it in safeItems)
                    freeItems.Enqueue(it);
            }
        }

        /// <summary>
        /// Sets the item to the recycle storage.
        /// </summary>
        /// <param name="item">The item to recycle.</param>
        /// <returns>
        ///   <c>True</c> if the item have been recycle; otherwise false;
        /// </returns>
        protected override bool SetToTheRecycleStorage(IPoolItem item)
        {
            Type iType = item.GetType();
            Queue<IPoolItem> freeItems = null;

            if (!this._freeItems.TryGetValue(iType, out freeItems))
            {
                freeItems = new Queue<IPoolItem>();
                this._freeItems.Add(iType, freeItems);
            }

            if (freeItems.Count < base.MaxItems)
            {
                freeItems.Enqueue(item);
                return true;
            }
            return false;
        }

        #endregion
    }
}
